package org.luxor.accesslog.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.luxor.accesslog.annotation.AccessLog;
import org.luxor.accesslog.config.AccessLogProperties;
import org.luxor.accesslog.entity.AccessLogEntity;
import org.luxor.accesslog.service.IAccessLogService;
import org.luxor.commons.core.utils.HttpContextUtils;
import org.luxor.commons.core.utils.SecurityContextUtils;
import org.luxor.commons.core.utils.StrKit;
import org.luxor.commons.core.utils.SystemTimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Date;

/**
 * AccessLog 切面处理类
 *
 * @author Mr.yan @date 2020/4/6
 */
@Aspect
@Order(-2)
public class AccessLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(AccessLogAspect.class);

    @Autowired
    private AccessLogProperties properties;
    @Autowired
    private IAccessLogService accessLogService;

    @Around("@within(org.luxor.accesslog.annotation.AccessLog)||@annotation(org.luxor.accesslog.annotation.AccessLog)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        if (!properties.isEnabled()) {
            return point.proceed();
        }
        AccessLog annotation = obtainAnnotation(point);
        if (annotation == null) {
            return point.proceed();
        }
        try {
            logger.debug("===审计日志===  module:{} / message:{}", annotation.module(), annotation.message());

            AccessLogEntity accessLog = new AccessLogEntity();
            accessLog.setUserName(SecurityContextUtils.getUsername());
            accessLog.setModule(obtainDefModule(point));
            accessLog.setIp(HttpContextUtils.getRemoteAddr(HttpContextUtils.getHttpServletRequest()));
            accessLog.setTime(new Date(SystemTimeUtils.now()));
            accessLog.setMessage(annotation.message());
            accessLogService.saveLog(accessLog);

            return point.proceed();
        } catch (Throwable e) {
            logger.warn("保存AccessLog日志出错", e);
            throw e;
        }
    }

    private AccessLog obtainAnnotation(ProceedingJoinPoint pjp) {
        Class<?> thisClass = pjp.getTarget().getClass();
        if (thisClass.isAnnotationPresent(AccessLog.class)) {
            return thisClass.getAnnotation(AccessLog.class);
        }

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        if (method.isAnnotationPresent(AccessLog.class)) {
            return method.getAnnotation(AccessLog.class);
        }
        return null;
    }


    private String obtainDefModule(ProceedingJoinPoint pjp) {
        Class<?> thisClass = pjp.getTarget().getClass();
        if (thisClass.isAnnotationPresent(AccessLog.class)) {
            AccessLog annotation = thisClass.getAnnotation(AccessLog.class);
            if (StringUtils.hasText(annotation.module())) {
                return annotation.module();
            } else {
                return thisClass.getSimpleName();
            }
        }

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        if (method.isAnnotationPresent(AccessLog.class)) {
            AccessLog annotation = method.getAnnotation(AccessLog.class);
            if (StringUtils.hasText(annotation.module())) {
                return annotation.module();
            } else {
                return method.getDeclaringClass().getSimpleName() + StrKit.DOT + method.getName();
            }
        }
        return null;
    }


}
